package com.hitqz.robot.biz.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.business.dto.*;
import com.hitqz.robot.api.business.enums.FormConfigEnum;
import com.hitqz.robot.api.business.enums.RobotOtherCommandEnum;
import com.hitqz.robot.api.business.enums.TicketsStatusEnum;
import com.hitqz.robot.api.common.dto.robot.DoorStatusDto;
import com.hitqz.robot.api.common.dto.robot.RobotMoveDto;
import com.hitqz.robot.api.common.dto.tickets.CtrlTicketsEvent;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.api.common.entity.*;
import com.hitqz.robot.api.dispatch.model.MessageDto;
import com.hitqz.robot.api.dispatch.protocol.ProtocolEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.*;
import com.hitqz.robot.api.dispatch.protocol.dto.CmdRobot2PosDto;
import com.hitqz.robot.api.dispatch.protocol.dto.RobotWarningDto;
import com.hitqz.robot.biz.business.dispatch.DispatchService;
import com.hitqz.robot.biz.config.LogTailer;
import com.hitqz.robot.biz.flow.util.LiteFlowUtil;
import com.hitqz.robot.biz.service.*;
import com.hitqz.robot.biz.util.PatrolConstant;
import com.hitqz.robot.camera.dto.DoorCtrlDto;
import com.hitqz.robot.common.core.constant.CommonConstants;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.driver.parent.RobotDriverService;
import com.hitqz.robot.netty.service.Send2ServerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;

import static com.hitqz.robot.biz.config.GuavaCacheConfig.CACHE_CONST;
import static com.hitqz.robot.biz.config.GuavaCacheConfig.REMOTE_CONTROL_AUTH;

/**
 * 监听来自服务端的消息，进行处理
 * @author xupkun
 * @date 2024/6/13
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class SeverRobotEventListener {

    private final RobotDriverService robotDriverService;

    private final Send2ServerService send2ServerService;

    private final RobotService robotService;

    private final RobotMapService robotMapService;

    private final RobotMapPosService robotMapPosService;

    private final TicketsService ticketsService;

    private final TicketsTaskService ticketsTaskService;

    private final DispatchService dispatchService;

    private final ButtonService buttonService;

    private final SysWarningService sysWarningService;


    private final FormConfigService formConfigService;

    private final Cache<String, Boolean>  robotWebRefreshCache;

    private final PatrolDevService patrolDevService;

    private final PatrolItemService patrolItemService;

    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5,10,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());



    @Async("poolTaskExecutor")
    @EventListener
    public void handlerQuery(RobotCmdNavDto robotCmdNavDto) {
        //log.info("调度系统要求导航状态 {}", JSONObject.toJSONString(robotCmdNavDto));
        robotDriverService.navCmd(robotCmdNavDto.getCmd());
    }

    @Async("poolTaskExecutor")
    @EventListener
    public void handlerQuery(RobotWarningDto updateWarningDto){
        if (updateWarningDto.getId()!=null){
            SysWarning sysWarning = sysWarningService.getById(updateWarningDto.getId());
            sysWarning.setStatus(1);
            sysWarningService.updateById(sysWarning);
        }
    }


    @Async("poolTaskExecutor")
    @EventListener
    public void handlerQuery(UpdateClientWarningDto dto){
        if (dto.getId()!=null){
            SysWarning sysWarning = sysWarningService.getById(dto.getId());
            sysWarning.setStatus(1);
            sysWarningService.updateById(sysWarning);
            PatrolConstant.webWarningStatus.set(false);
        }
        if (dto.getReadAll()!=null && dto.getReadAll()){
            List<SysWarning> list = sysWarningService.list(Wrappers.<SysWarning>lambdaQuery().eq(SysWarning::getStatus, 0));
            list.forEach(f->f.setStatus(1));
            sysWarningService.updateBatchById(list);
            PatrolConstant.webWarningStatus.set(false);
        }
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(MessageDto dto) {
        if (dto.getCmd() == ProtocolEnum.ROBOT_SYNC_CMD.getCmd()) {
            RobotAsyncCmdDto robotAsyncCmdDto = JSONObject.parseObject(dto.getData(), RobotAsyncCmdDto.class);
            Robot robot = robotService.getDefaultRobot();
            if(robotAsyncCmdDto == null || robotAsyncCmdDto.getCmd() == null) {
                return;
            }
            if (robotAsyncCmdDto.getCmd() == 100) {
                // 同步地图
                robotMapService.syncMap();
            }else if (robotAsyncCmdDto.getCmd() == 101) {
                // 同步机器人消息
                robotService.syncRobot();
            }else if (robotAsyncCmdDto.getCmd() == 102){
                // 同步点位
                robotMapPosService.syncMapPos();
            }else if (robotAsyncCmdDto.getCmd() == 103) {
               // 方便 后续改为 消息一起同步 上传
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                for (FormConfigEnum formConfigEnum : FormConfigEnum.values()) {
                    if (formConfigEnum.getFormName().contains("tickets_params")
                            && formConfigEnum.getFormName().contains(robot.getRobotModel().toLowerCase())
                    ) {
                        log.info("上传动态表单内容：{},{}",formConfigEnum.getFormTitle(),formConfigEnum.getFormName());
                        formConfigService.syncFormMoal(formConfigEnum.getFormName());
                    }
                }
            }else if (robotAsyncCmdDto.getCmd() == 104){
                //同步设备
                patrolDevService.syncDev();
            }else if (robotAsyncCmdDto.getCmd() == 105){
                //哨兵模式开
                PatrolConstant.sentinelModeEnable.set(true);
                log.info("开启哨兵模式");
            }else if (robotAsyncCmdDto.getCmd() == 106){
                //哨兵模式关
                PatrolConstant.sentinelModeEnable.set(false);
                log.info("关闭哨兵模式");
            }
            send2ServerService.commonReply(dto.getSn(), R.ok());
        }
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(CmdRobot2PosDto dto) {
        String posCode = dto.getPosCode();
        RobotMapPos robotMapPos = robotMapPosService.getOne(Wrappers.<RobotMapPos>lambdaQuery()
                .eq(RobotMapPos::getCode, posCode));
        log.info("上位机指定下发" + posCode);
        dispatchService.go2TargetPosition(robotMapPos, false);
    }


    @Async("ctrlExecutor")
    @EventListener
    public void handlerRobotCtrl(RobotControlButtonDto dto) {
        //TODO 机器人控制
        log.info("====>>> {}", JSONObject.toJSONString(dto));
        if (Objects.equals(dto.getAction(), "speakStart")) {
            HttpUtil.get("http://127.0.0.1:9088/speaker/start",5000);
        } else if (Objects.equals(dto.getAction(), "speakStop")) {
            HttpUtil.get("http://127.0.0.1:9088/speaker/stop",5000);
        } else if (Objects.equals(dto.getAction(), "goForward")) {
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setLinerSpeed("0.2");
            robotMoveDto.setDist("0.2");
            robotMoveDto.setLinerSpeedY("0");
            robotDriverService.setSpeed(robotMoveDto);
        } else if (Objects.equals(dto.getAction(), "backForward")) {
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setLinerSpeed("-0.2");
            robotMoveDto.setDist("0.2");
            robotMoveDto.setLinerSpeedY("0");
            robotDriverService.setSpeed(robotMoveDto);
        }else if (Objects.equals(dto.getAction(), "leftTranslate")){
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setLinerSpeed("0");
            robotMoveDto.setDist("0.2");
            robotMoveDto.setLinerSpeedY("0.2");
            robotDriverService.setSpeed(robotMoveDto);
        }else if (Objects.equals(dto.getAction(), "rightTranslate")){
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setLinerSpeed("0");
            robotMoveDto.setDist("0.2");
            robotMoveDto.setLinerSpeedY("-0.2");
            robotDriverService.setSpeed(robotMoveDto);
        }else if (Objects.equals(dto.getAction(), "left")) {
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setAngle("0.15");
            robotMoveDto.setDist("0");
            robotMoveDto.setAngularSpeed("0.25");
            robotDriverService.setSpeed(robotMoveDto);
        } else if (Objects.equals(dto.getAction(), "right")) {
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setAngle("0.15");
            robotMoveDto.setDist("0");
            robotMoveDto.setAngularSpeed("-0.25");
            robotDriverService.setSpeed(robotMoveDto);
        } else if (Objects.equals(dto.getAction(), "stop")) {
            robotWebRefreshCache.put("webRefresh", true);
            RobotMoveDto robotMoveDto = new RobotMoveDto();
            robotMoveDto.setLinerSpeed("0");
            robotMoveDto.setAngle("0");
            robotMoveDto.setDist("0");
            robotMoveDto.setLinerSpeedY("0");
            robotDriverService.setSpeed(robotMoveDto);
            CtrlTicketsEvent ctrlTicketsEvent = new CtrlTicketsEvent();
            ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_STOP);
            ctrlTicketsEvent.setMessage("上位机急停中断任务");
            ctrlTicketsEvent.setTicketsId(LiteFlowUtil.ticketsId);
            SpringUtil.getApplicationContext().publishEvent(ctrlTicketsEvent);
		}else if (Objects.equals(dto.getAction(), "charge")) {
            robotDriverService.cmdCharge(1);
        }else if (Objects.equals(dto.getAction(), "cancelCharge")) {
            robotDriverService.cmdCharge(0);
        }else if (Objects.equals(dto.getAction(),"speedUp")){
            robotDriverService.changeWebControlSpeed(0.1f);
        }else  if (Objects.equals(dto.getAction(),"speedDown")){
            robotDriverService.changeWebControlSpeed(-0.1f);
        }else if (Objects.equals(dto.getAction(),"powerOnOrOff")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.POWER_ON_OR_OFF,"");
        }else if (Objects.equals(dto.getAction(),"gf-powerOnOrOff")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_IO,JSONObject.toJSONString(dto.getParam()));
        }else if (Objects.equals(dto.getAction(),"gf-relay")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_RELAY,JSONObject.toJSONString(dto.getParam()));
        }else if (Objects.equals(dto.getAction(),"gf-function")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_FUNCTION,JSONObject.toJSONString(dto.getParam()));
        }else if (Objects.equals(dto.getAction(),"openDoor") || Objects.equals(dto.getAction(),"closeDoor")){
            DoorStatusDto doorStatusDto = new DoorStatusDto();
            doorStatusDto.setSn(1);
            doorStatusDto.setStatus(dto.getAction().contains("open")?1:0);
            doorStatusDto.setAsync(false);
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_DOOR_STATUS, JSONObject.toJSONString(doorStatusDto));
        }else if (Objects.equals(dto.getAction(),"goHome")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.GO_HOME, null);
        }else if (Objects.equals(dto.getAction(),"relocation")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.RELOCATION, null);
        }else if (Objects.equals(dto.getAction(),"jackingUp")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.JACKING_UP,null);
        }else if (Objects.equals(dto.getAction(),"jackingDown")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.JACKING_DOWN,null);
        }else if (Objects.equals(dto.getAction(),"set-light")){
            robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_LIGHT,null);
        }else if (Objects.equals(dto.getAction(),"voiceRequest")){
            Map<String,Integer> param =(Map<String,Integer>) dto.getParam();
            Integer cmd = param.get("cmd");
            if (Objects.equals(cmd,1)){
                PatrolConstant.padVoiceRequest.compareAndSet(0,1);
                robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_SPEAK_REQUEST,"1");
            }
            if (Objects.equals(cmd,0)){
                PatrolConstant.padVoiceRequest.compareAndSet(1,0);
                robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_SPEAK_REQUEST,"0");
            }

        } else if (Objects.equals(dto.getAction(), "remoteControlAuth")) {
            //获取遥控控制权
            Cache<String, Boolean> remoteControlAuthCache = SpringUtil.getBean(REMOTE_CONTROL_AUTH + CACHE_CONST);
            remoteControlAuthCache.put(REMOTE_CONTROL_AUTH, true);
            Tickets query = new Tickets();
            query.setStatus(TicketsStatusEnum.STATUS_EXECUTING.getCode());
            List<Tickets> list = ticketsService.lambdaQuery(query).list();
            for (Tickets tickets : list) {
                CtrlTicketsEvent ctrlTicketsEvent = new CtrlTicketsEvent();
                ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_PAUSE);
                ctrlTicketsEvent.setMessage("手动下发任务暂停");

                ctrlTicketsEvent.setTicketsId(tickets.getId().intValue());
                SpringUtil.getApplicationContext().publishEvent(ctrlTicketsEvent);
            }

        } else if (Objects.equals(dto.getAction(), "releaseRemoteControlAuth")) {
            //释放遥控控制权

            Cache<String, Boolean> remoteControlAuthCache = SpringUtil.getBean(REMOTE_CONTROL_AUTH + CACHE_CONST);
            remoteControlAuthCache.invalidate(REMOTE_CONTROL_AUTH);
        }else if (Objects.equals(dto.getAction(), "closeVisionWarning")){
            PatrolConstant.visionWaringFlag.set(false);
            log.info("关闭视觉告警声音");
        }

    }


    @Async("poolTaskExecutor")
    @EventListener
    public void handlerRobotInfo(RobotDto dto) {
        //TODO 机器人控制
        //log.info("收到机器人的模型请求：" + dto.getChargeThreshold());
        Robot robot = robotService.getDefaultRobot();
        if (Objects.equals(robot.getRobotSn(), dto.getRobotSn())) {
            log.info("收到机器人的模型请求：" + dto.getChargeThreshold());
            BeanUtil.copyProperties(dto, robot, false);
            robotService.updateById(robot);
        }
    }

    @Async("poolTaskExecutor")
    @EventListener
    public void handlerNextStop(RobotNextStepDto dto) {
        Cache<String, Boolean> nextStep = SpringUtil.getBean("nextStep");
        Robot robot = robotService.getDefaultRobot();
        nextStep.put(robot.getRobotSn(), true);
        log.info("收到执行下一步");
    }


    @Async("poolTaskExecutor")
    @EventListener
    public void handleTickets(RobotCmdTicketsDto dto) {
        Robot robot = robotService.getDefaultRobot();
        // 生成一个票数据
        Tickets tickets;
        if (Objects.equals(dto.getCmd(),0)){
            tickets = new Tickets();
            tickets.setAllotRobotName(robot.getRobotName());
            tickets.setAllotRobotSn(robot.getRobotSn());
            tickets.setAllotStatus(TicketsStatusEnum.ALLOT_ALLOCATION.getCode());
            tickets.setStatus(TicketsStatusEnum.STATUS_NONE.getCode());
            tickets.setName(dto.getTaskName());
            tickets.setContent(dto.getTaskContent());
            tickets.setCreateTime(new Date());
            tickets.setUpdateTime(new Date());
            ticketsService.save(tickets);
            send2ServerService.commonReply((int) ProtocolEnum.ROBOT_CMD_TICKETS.getCmd(), R.ok());
            // 同步工单 数据量小可以快速获取tickets的serverId回复
            try {
                Thread.sleep(500);
                //同步工单给服务器
                log.info("上传工单给服务端");
                send2ServerService.syncTickets(robot, tickets, ticketsTaskService.list(
                        Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId())
                ));
                //延时，确保时序可以更新serverId
                Thread.sleep(1000);
                //重新获取下工单，获取到serverId
                tickets = ticketsService.getById(tickets.getId());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 生成多个工单任务
            if (!ticketsTaskService.createTicket(dto.getTaskContent(), tickets)) {
                log.info(" 创建工单任务失败，不执行工单");
                return;
            }
            // 同步工单任务
            log.info("上传工单任务给服务端");
            send2ServerService.syncTickets(robot, tickets, ticketsTaskService.list(
                    Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId())
            ));
            // 执行任务
            Tickets finalTickets = tickets;
            threadPool.execute(()->{
                try {
                    //延时，确保时序可以更新工单任务的serverId
                    Thread.sleep(3000);
                    R result =  ticketsService.executeFlowChain(finalTickets.getId().intValue(),true);
                    log.info("开始执行：" + result.toString());
                    if (result.getCode() == CommonConstants.FAIL) {
                        AddWarningDto addWarningDto = new AddWarningDto();
                        addWarningDto.setCode("0");
                        addWarningDto.setTitle("任务执行异常");
                        addWarningDto.setContent(result.getMsg());
                        addWarningDto.setSuggestion("请确认状态后,再启动任务。");
                        addWarningDto.setLevel(1);
                        SpringUtil.getApplicationContext().publishEvent(addWarningDto);
                    }
                } catch (Exception e) {
                    log.error("启动流程失败");
                }
            });

        }else {

            tickets = ticketsService.getOne(Wrappers.<Tickets>lambdaQuery().eq(Tickets::getServerId,dto.getServerId()).last("limit 1"));
            CtrlTicketsEvent ctrlTicketsEvent = new CtrlTicketsEvent();
            if (Objects.equals(dto.getCmd(),1)) {
                ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_PAUSE);
                ctrlTicketsEvent.setMessage("手动下发任务暂停");
            }else if (Objects.equals(dto.getCmd(),2)) {
                ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_RESUME);
                ctrlTicketsEvent.setMessage("手动下发任务恢复");
            }else if (Objects.equals(dto.getCmd(),3)) {
                ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_STOP);
                ctrlTicketsEvent.setMessage("手动下发任务终止");
            }
            ctrlTicketsEvent.setTicketsId(tickets.getId().intValue());
            SpringUtil.getApplicationContext().publishEvent(ctrlTicketsEvent);

        }

    }


    @EventListener
    public void listenerSysInfo(ModifySysInfoDto sysInfoDto) {
        log.info("修改系统消息:" + sysInfoDto.getSysName());

    }

    @EventListener
    public void listenerTickets(RobotTicketsDto dto) {
        // 此处目前只用来执行端保存服务端的server id,避免修改其他字段，造成同步问题
        Tickets tickets = ticketsService.getById(dto.getClientTicketsId());
        if (tickets == null) {
            return;
        }
        if (tickets.getServerId()==null) {
            tickets.setServerId(dto.getServerId());
            ticketsService.updateById(tickets);
            log.info("工单client id:{},更新 server id:{}",dto.getClientTicketsId(),dto.getServerId());
        }
        List<TicketsTask> list = new ArrayList<>();
        for (RobotTicketsTaskDto taskDto : dto.getTickets()) {
            TicketsTask ticketsTask = ticketsTaskService.getById(taskDto.getClientTaskId());
            if (ticketsTask!=null && ticketsTask.getServerTaskId()==null) {
                ticketsTask.setServerTaskId(taskDto.getServerId().longValue());
                list.add(ticketsTask);
            }
        }
        if (!list.isEmpty()){
            ticketsTaskService.updateBatchById(list);
            log.info("批量更新client id为 {}的工单的{}个任务的server id",list.size(),dto.getClientTicketsId());
        }

    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(ReportLogMessageDto dto) {
        LogTailer.pushNow(dto.getClientId(), dto.getRandomId());
    }

    @Async("ctrlExecutor")
    @EventListener
    public void handlerEquipmentCtrl(RobotEquipmentControlButtonDto dto){
        log.info("server button: {}", dto.getAction());
        buttonService.handleEquipmentButton(dto,null);
    }


    @Async("poolTaskExecutor")
    @EventListener
    public void onListenerWarnings(PatrolItemStandardSyncDto dto){
        log.info("同步巡检项标准size:{}",dto.getStandardList().size());
        List<PatrolItem> list = new ArrayList<>();
        for (PatrolItemStandardDto standardDto : dto.getStandardList()) {
            PatrolItem patrolItem = new PatrolItem();
            patrolItem.setId(standardDto.getItemId());
            patrolItem.setStandardValue(standardDto.getStandardValue());
            list.add(patrolItem);
        }
        if (!list.isEmpty()){
            patrolItemService.updateBatchById(list);
        }

    }



}
